package com.manbu.live.user.provider.service.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.manbu.live.common.interfaces.vo.WebResponseVO;
import com.manbu.live.farmework.redis.starter.keys.UserProviderCacheKeyBuilder;
import com.manbu.live.common.interfaces.utils.ConvertBeanUtils;
import com.manbu.live.common.interfaces.utils.RedisTimeUtils;
import com.manbu.live.msg.dto.MsgCheckDTO;
import com.manbu.live.msg.interfaces.ISmsRpc;
import com.manbu.live.user.dto.UserDTO;
import com.manbu.live.user.dto.UserLoginDTO;
import com.manbu.live.user.inerface.IUserPhoneRpc;
import com.manbu.live.user.provider.dao.mapper.IUserMapper;
import com.manbu.live.user.provider.dao.po.UserPO;
import com.manbu.live.user.provider.service.IUserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.MQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author hanWang
 * @Title:
 * @Package
 * @Description:
 * @date 2024/1/179:19 下午
 */
@Service
public class UserServiceImpl implements IUserService {

    @Resource
    private IUserMapper userMapper;
    @Resource
    private RedisTemplate<String,UserDTO> redisTemplate;

    @Resource
    private UserProviderCacheKeyBuilder keyBuilder;
    @Resource
    private MQProducer producer;
    @DubboReference
    private ISmsRpc iSmsRpc;
    @Resource
    private IUserPhoneRpc phoneRpc;
    @Override
    public UserDTO getByUserId(Long userId) {


        if (userId ==null){

            return null;
        }
        String key = "userInfo:"+userId;
       // String key = keyBuilder.buildUserInfoKey(userId);


        UserDTO userDTO = redisTemplate.opsForValue().get(key);
        if (userDTO == null){

            UserDTO convert = ConvertBeanUtils.convert(userMapper.selectById(userId), UserDTO.class);
            if (convert !=null){
                redisTemplate.opsForValue().set(key,convert,RedisTimeUtils.createRandomExpireTime(), TimeUnit.SECONDS);
            }
            return convert;
        }


        return userDTO;

    }

    @Override
    public Boolean deleteUserById(Long userId) {

        return userMapper.deleteById(userId)>0;
    }

    @Override
    public Boolean updateUserById(UserDTO userDTO) {
        try {
            UserPO convert = ConvertBeanUtils.convert(userDTO, UserPO.class);
            int i = userMapper.updateById(convert);
            String userInfoKey = keyBuilder.buildUserInfoKey(userDTO.getUserId());
            redisTemplate.delete(userInfoKey);
            Message message = new Message();
            message.setTopic("user-updaate-catch");
            message.setDelayTimeLevel(1);
            message.setBody(JSON.toJSONString(userDTO).getBytes( ));
            producer.send(message);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return true;
    }

    @Override
    public Boolean saveUser(UserDTO userDTO) {
        UserPO convert = ConvertBeanUtils.convert(userDTO, UserPO.class);
        return userMapper.insert(convert)>0;
    }

    @Override
    public Map<Long, UserDTO> bathQueryUserInfo(List<Long> userIdList) {
        if (CollectionUtils.isEmpty(userIdList)){

            return Maps.newHashMap();
        }
        userIdList = userIdList.stream().filter(id -> id > 100000).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(userIdList)){

            return Maps.newHashMap();
        }
//        userIdList.forEach(id->{
//            // 不推荐 性能很差
//            UserDTO userDTO = redisTemplate.opsForValue().get(id);
//
//
//        });
        List<String> keyList = userIdList.stream().map(id -> {
            // 不推荐 性能很差
            String s = keyBuilder.buildUserInfoKey(id);
            return s;

        }).collect(Collectors.toList());

        List<UserDTO> userDTOList = redisTemplate.opsForValue().multiGet(keyList).stream().filter(userDTO -> userDTO!=null).collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(userDTOList)&& userDTOList.size() == userIdList.size()){
            userDTOList.stream().collect(Collectors.toMap(UserDTO::getUserId, Function.identity(),(x, y)->x));
        }

        // 取出所有没有命中缓存的id
        List<Long> userIdCatch = userDTOList.stream().map(userDTO -> userDTO.getUserId()).collect(Collectors.toList());
        List<Long> userIdNotinCatch = userIdList.stream().filter(userId -> !userIdCatch.contains(userId)).collect(Collectors.toList());

        Map<Long, List<Long>> userIdMap = userIdNotinCatch.stream().collect(Collectors.groupingBy(userid -> userid % 100));
        //多线程查询数据库 对java配置有要求 需要java堆开的大一些 能够开启足够多的线程去承载服务
        List<UserDTO> dbQueryList = new CopyOnWriteArrayList<>();
        userIdMap.values().parallelStream().forEach(queryIdList->{
            dbQueryList.addAll(ConvertBeanUtils.convertList(userMapper.selectBatchIds(queryIdList),UserDTO.class));
        });
        // 根据场景是否决定把用户放入缓存
        if (!CollectionUtils.isEmpty(dbQueryList)) {
            Map<String, UserDTO> saveCaatchMap = dbQueryList.stream().collect(Collectors.toMap(userDTO -> keyBuilder.buildUserInfoKey(userDTO.getUserId()), Function.identity(), (x, y) -> x));
            redisTemplate.opsForValue().multiSet(saveCaatchMap);
            // 通过管道方式设置key过期 时间 开销性能小
            redisTemplate.executePipelined(new SessionCallback<Object>() {

                @Override
                public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
                     for (String key:saveCaatchMap.keySet()){
                         operations.expire((K) key,RedisTimeUtils.createRandomExpireTime(),TimeUnit.SECONDS);
                     }
                     return null;
                }
            });
        }

//        dbQueryList.forEach(userDTO -> {
//            redisTemplate.opsForValue().set(keyBuilder.buildUserInfoKey(userDTO.getUserId()),userDTO);
//        });
        userDTOList.addAll(dbQueryList);
        return userDTOList.stream().collect(Collectors.toMap(UserDTO::getUserId, Function.identity(),(x, y)->x));
    }

    @Override
    public WebResponseVO login(String phone, Integer code, HttpServletResponse response) {
        if (StringUtils.isEmpty(phone)){
            return WebResponseVO.errorParam("手机号不能为空");
        }
        if (code==null ||  code<10000){
            return WebResponseVO.errorParam("验证码格式错误");
        }
        MsgCheckDTO msgCheckDTO = iSmsRpc.checkLoginCode(phone, code);
        if (!msgCheckDTO.isCheckStatus()){
            return WebResponseVO.bizError(msgCheckDTO.getDesc());
        }

        if (StringUtils.isEmpty(phone)){
            return WebResponseVO.errorParam("手机号不能为空");
        }
        UserLoginDTO login = phoneRpc.login(phone);

        Cookie cookie = new Cookie("mb-token",login.getToken());
        cookie.setPath("/");
        cookie.setMaxAge(3600*24*10);
        cookie.setDomain("faseplay.club");
        response.addCookie(cookie);
        return WebResponseVO.success(login);
    }
}
